package com.ds.infra.rpc.core.protocol;

import com.ds.infra.rpc.core.constant.RpcConstants;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.io.Serial;
import java.io.Serializable;

/**
 * 作用：RPC通信协议封装类
 * RPC协议封装类（协议头 + 消息体）
 * +---------------------------------------------------------------+
 * | 魔数(2B) | 版本(1B) | 消息类型(1B) | 长度(4B) | 数据内容(N bytes) |
 * +---------------------------------------------------------------+
 *
 * @author WeiShaoying
 */
@Data
@NoArgsConstructor
public class RpcProtocol<T> implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    /**
     * 协议头
     */
    private Header header;

    /**
     * 消息体（泛型支持不同消息类型）
     */
    private T body;

    public RpcProtocol(Header header, T body) {
        header.validate(); // 构造时校验
        this.header = header;
        this.body = body;
    }

    /**
     * 协议头定义（内部静态类）
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Header implements Serializable {

        @Serial
        private static final long serialVersionUID = 8670922570421435749L;
        /**
         * 魔数（固定0xCAFE用于快速识别无效包）
         */
        private short magic = (short) 0xCAFE;

        /**
         * 协议版本（当前版本1）
         */
        private byte version = 1;

        /**
         * 消息类型（0-请求, 1-响应, 2-心跳）
         */
        private byte msgType;

        /**
         * 消息体长度（单位：字节）
         */
        private int msgLen;

        // 便捷构造方法（用于心跳等场景）
        public Header(byte msgType, int msgLen) {
            this(RpcConstants.MAGIC, (byte) 1, msgType, msgLen);
        }

        public void validate() {
            if (this.magic != (short) 0xCAFE) {
                throw new IllegalArgumentException("Magic number must be 0xCAFE");
            }
            if (this.version != 1) {
                throw new IllegalArgumentException("Unsupported protocol version");
            }
        }
    }

    /**
     * 消息类型枚举
     */
    @Getter
    public enum MsgType {

        REQUEST((byte) 0),
        RESPONSE((byte) 1),
        HEARTBEAT((byte) 2);

        private final byte value;

        MsgType(byte value) {
            this.value = value;
        }

    }
}
